package com.gzf.mongodb.dao;

import com.gzf.mongodb.model.User;
import com.gzf.mongodb.utils.MongoDBUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import org.bson.Document;
import org.bson.conversions.Bson;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;

public class MongoDao
{
    public void findAll()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        FindIterable<Document> iterable = collection.find();

        for(Document doc: iterable)
        {
            System.out.println(doc.toJson());
        }
    }

    public void findByFieldCondition()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        Document queryDoc = new Document("stu_age", new Document("$lt", 25));

        FindIterable<Document> documents = collection.find(queryDoc);

        for(Document document : documents)
        {
            System.out.println(document);
        }
    }

    public void findByAndCondition()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        Document documentDoc = new Document("stu_age", new Document("$eq", 21)).append("stu_name", "zhangsan");

        FindIterable<Document> documents = collection.find(documentDoc);

        for(Document document : documents)
        {
            System.out.println(document);
        }
    }

    public void findByOrCondition()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        Document document = new Document("$or", Arrays.asList(new Document("stu_name", "zhangsan"), new Document("name", "sunfei")));

        FindIterable<Document> documents = collection.find(document);

        for(Document doc : documents)
        {
            System.out.println(doc);
        }
    }

    //通过Filters查询
    public void findByFilters()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

//        FindIterable<Document> documents = collection.find(Filters.in("stu_age", Arrays.asList(21,25,27)));

//        FindIterable<Document> documents = collection.find(Filters.lte("stu_age", 23));

       /* Bson bson = Filters.and(Filters.eq("stu_name", "zhangsan"), Filters.eq("stu_age", 21));

        FindIterable<Document> documents = collection.find(bson);*/

        FindIterable<Document> documents = collection.find(Filters.or(Filters.eq("stu_name","zhangsan"),Filters.eq("name", "sunfei")));

        for(Document doc : documents)
        {
            System.out.println(doc);
        }
    }

    public void countStudents()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        long countRow = collection.count(Filters.eq("stu_age", 21));
        System.out.println(countRow);
    }

    public void sortedStudent()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        //按name升序
        //FindIterable<Document> documents = collection.find().sort(Sorts.ascending("stu_name"));
        //按age降序
        //FindIterable<Document> documents = collection.find().sort(Sorts.descending("age"));
        //按name升序，name相同的按age降序
        FindIterable<Document> documents = collection.find().sort(Sorts.orderBy(Sorts.ascending("stu_name"), Sorts.descending("stu_age")));

        for(Document doc : documents)
        {
            System.out.println(doc);
        }
    }

    public void skipsAndLimit()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        //跳过前5条(0-4)，返回(5-9)共5条。
        FindIterable<Document> documents = collection.find().sort(Sorts.descending("stu_age")).skip(5).limit(5);

        for(Document doc : documents)
        {
            System.out.println(doc);
        }
    }

    public void distinctStudents()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        DistinctIterable<String> name = collection.distinct("stu_name", String.class);

        for (String s : name)
        {
            System.out.println(s);
        }

        DistinctIterable<Integer> age = collection.distinct("stu_age", Integer.class);

        for (Integer a : age)
        {
            System.out.println(a);
        }

    }

    public void addStudent()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        Document doc = new Document();
        doc.put("name", "zhangsan");
        doc.put("age", 40);
        collection.insertOne(doc);
    }

    public void addStudents()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        List<Document> docs = new LinkedList<Document>();

        for(int i=0; i<10; i++){
            Document doc = new Document();
            doc.put("name", "zhangsan"+i);
            doc.put("age", 40+i);
            docs.add(doc);
        }
        collection.insertMany(docs);
    }

    public void updateStudent()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        collection.updateMany(Filters.eq("age", 45), new Document("$set", new Document("age", 16).append("name","xxx45")));
    }

    public void deleteStudent()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("students");

        //删除第一个符合条件的
        collection.deleteOne(Filters.eq("age", 17));
        //删除所有符合条件的
        //collection.deleteMany(Filters.eq("age", 17));
    }

    /**
     * 以流水线的方式，分阶段处理document。
     * 主要阶段：
     * $project
     * 对流入的每个document进行投影操作，类似于select field1, field2, ...
     * 可以添加新字段、或删除已存在的某些字段。
     */
    public void insertStudents()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("student");

        collection.insertOne(new Document("name", "csc").append("hobby", Arrays.asList("reading", "coding")));
        collection.insertOne(new Document("name", "nicky").append("hobby", Arrays.asList("game")));
        collection.insertOne(new Document("name", "jack").append("hobby", Arrays.asList("movie")));
        collection.insertOne(new Document("name", "tom").append("hobby", Arrays.asList("reading", "coding")));
        collection.insertOne(new Document("name", "lucy").append("hobby", Arrays.asList("reading", "football")));
        collection.insertOne(new Document("name", "lion").append("hobby", Arrays.asList("basketball", "football")));

        AggregateIterable<Document> aggregate = collection.aggregate(Arrays.asList(new Document("$project", new Document("name", 1).append("_id", 0).append("hobby", 1))));
        //只有_id默认输出的，不想输出该字段，需要设置为0，需要输出的其他字段需要手动设置为1.
        //输出结果：
        for (Document document : aggregate)
        {
            System.out.println(document.toJson());
        }
    }

    /**
     * $unwind
     *把数组中的元素拆分为多个document
     */
    public void splitDocument()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("student");

        AggregateIterable<Document> aggregate = collection.aggregate(Arrays.asList(
                new Document("$project", new Document("name", 1).append("_id", 0).append("hobby", 1))
                ,new Document("$unwind", "$hobby")
        ));

        for(Document document : aggregate)
        {
            System.out.println(document.toJson());
        }

    }

    /**
     * 类似于group by，可以使用各种聚合操作符，常用的有：
     *$sum, $avg, $max, $min, $first, $last
     */
    public void testGroup()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("student");

        //计算每个hobby的人数
        AggregateIterable<Document> aggregate = collection.aggregate(Arrays.asList(
                new Document("$project", new Document("name", 1).append("_id", 0).append("hobby", 1))
                ,new Document("$unwind", "$hobby")
                ,new Document("$group", new Document("_id", "$hobby").append("count", new Document("$sum", 1)))
        ));

        for(Document document : aggregate)
        {
            System.out.println(document.toJson());
        }

        System.out.println("---------------------------------------");
        //必须首先设置("_id", "$hobby")，这句的含义是group by hobby这个字段。引用字段一定要添加$打头。
        //如：输出每个hobby的第一个name: ("$fisrt","$name")。
        aggregate = collection.aggregate(Arrays.asList(
                new Document("$project", new Document("name", 1).append("_id", 0).append("hobby", 1))
                ,new Document("$unwind", "$hobby")
                ,new Document("$group", new Document("_id", "$hobby").append("count", new Document("$sum", 1)).append("first", new Document("$first", "$name")))
        ));

        for(Document document : aggregate)
        {
            System.out.println(document.toJson());
        }
    }

    public void testMapReduceFunction()
    {
        MongoDatabase db = MongoDBUtils.getMongoDatabase();

        MongoCollection<Document> collection = db.getCollection("student");

        //统计每个年龄的人数，原理同hadoop中的map、reduce方法。
        //定义一个js function，以年龄作为key,进行计数。this表示当前document
        String map = "function(){emit(this.age, 1)}";//age : [1,1,1,1]
        //js function, 对年龄的所有计数，累加返回key-value
        String reduce = "function(key, values){ return Array.sum(values)}";
        MapReduceIterable<Document> docs = collection.mapReduce(map, reduce);
        for (Document doc : docs) {
            System.out.println(doc.toJson());
        }
    }
}
